package com.gbas.mus.model;

import com.gbas.model.Naipe;
import com.gbas.model.VALOR;
import com.gbas.mus.JugarMus;
import com.gbas.mus.model.lances.*;

import java.util.*;

public abstract class Fase {
    Ronda ronda;

    public List<Lance> lances;
    Set<JugadorMus> turnoPara = new HashSet<JugadorMus>();


    public Fase(Ronda ronda) {
        this.ronda = ronda;
        lances = new ArrayList<Lance>();
    }

    public List<Lance> getLances() {
        return lances;
    }

    public Set<JugadorMus> getTurno() {
        return turnoPara;
    }

    public Jugadores jugs() {
        return ronda.juego.vaca.partida.jugadores;
    }

    public void limpiaAccion() {
        turnoPara.clear();
    }

    public void permiteAccion(JugadorMus j, OP_INPUT opciones) {
        permiteAccion(j, opciones, true);
    }

    public void permiteAccion(JugadorMus j, OP_INPUT opciones, boolean clear) {
        if (clear) {
            limpiaAccion();
        }
        turnoPara.add(j);
        j.sendMessage(opciones);
    }

    public abstract boolean lanceValido(Lance lance);

    public abstract void doLance(Lance lance);

    public abstract String getNombre();

    public boolean isJugadorLanceValid(Lance lance) {
        return turnoPara.contains(lance.getJugador());
    }

    public void addLance(Lance lance) {
        if (isJugadorLanceValid(lance)) {
            if (lanceValido(lance)) {
                if (lance instanceof LanceAceptoOrdago || lance instanceof LanceEnvido || lance instanceof LanceOrdago || lance instanceof LanceVer) {
                    cancelaRespuestaCompanero(lance.getJugador());
                }
                // Registra la accion
                lances.add(lance);
                doLance(lance);
            } else {
                // TODO Error accion no valida
            }
        } else {
            // TODO Error jugador no valido
        }
    }

    public boolean esLanceDeJuego(Lance lance) {
        final Lance lastLance = getLastLanceContrario(lance.getJugador());
        // Ordago y AceptoOrdago
        // Envido, Paso, Veo
        if (lance instanceof LanceAceptoOrdago) {
            return lastLance != null && lastLance instanceof LanceOrdago;
        } else if (lance instanceof LanceOrdago) {
            return lastLance == null || lastLance instanceof LanceEnvido || lastLance instanceof LancePaso;
        } else if (lance instanceof LanceEnvido) {
            return lastLance == null || lastLance instanceof LanceEnvido || lastLance instanceof LancePaso;
        } else if (lance instanceof LancePaso) {
            return lastLance == null || lastLance instanceof LanceEnvido || lastLance instanceof LancePaso || lastLance instanceof LanceOrdago;
        } else if (lance instanceof LanceVer) {
            return lastLance != null && lastLance instanceof LanceEnvido;
        } else {
            /// TODO Error de mensaje
            return false;
        }
    }

    public Lance getLastLanceContrario(JugadorMus j) {
        for (int nLance = lances.size() - 1; nLance >= 0; nLance--) {
            final Lance lastLance = lances.get(nLance);
            if (lastLance.getJugador().equipo != j.equipo) {
                return lastLance;
            }
        }
        return null;
    }

    public void turnoAMano(OP_INPUT op) {
        permiteAccion(jugs().getJugador(), op);
    }

    public void turnoASiguienteJugador(JugadorMus jugador, OP_INPUT op) {
        permiteAccion(jugs().getNextJugador(jugador), op);
    }

    public void turnoASiguienteJugadorConPar(JugadorMus jugador, OP_INPUT op) {
        JugadorMus next = jugs().getNextJugador(jugador);
        while (!hayPar(next.getNaipes())) {
            next = jugs().getNextJugador(next);
        }
        permiteAccion(next, op);
    }

    public void turnoASiguienteJugadorConJuego(JugadorMus jugador, OP_INPUT op) {
        JugadorMus next = jugs().getNextJugador(jugador);
        while (!hayJuego(next.getNaipes())) {
            next = jugs().getNextJugador(next);
        }
        permiteAccion(next, op);
    }

    public void turnoASiguientePareja(JugadorMus jugador, OP_INPUT op) {
        final JugadorMus jNext = jugs().getNextJugador(jugador);
        permiteAccion(jNext, op);
        permiteAccion(jNext.pareja, op, false);
    }

    public void turnoASiguienteParejaPares(JugadorMus jugador, OP_INPUT op) {
        final JugadorMus jNext = jugs().getNextJugador(jugador);
        if (hayPar(jNext.getNaipes())) {
            permiteAccion(jNext, op);
            if (hayPar(jNext.pareja.getNaipes())) {
                permiteAccion(jNext.pareja, op, false);
            }
        } else {
            if (hayPar(jNext.pareja.getNaipes())) {
                permiteAccion(jNext.pareja, op);
            } else {
                // Error nadie tiene par???
            }
        }

    }

    public void turnoASiguienteParejaJuego(JugadorMus jugador, OP_INPUT op) {
        final JugadorMus jNext = jugs().getNextJugador(jugador);
        if (hayJuego(jNext.getNaipes())) {
            permiteAccion(jNext, op);
            if (hayJuego(jNext.pareja.getNaipes())) {
                permiteAccion(jNext.pareja, op, false);
            }
        } else {
            if (hayJuego(jNext.pareja.getNaipes())) {
                permiteAccion(jNext.pareja, op);
            } else {
                // Error nadie tiene par???
            }
        }

    }

    public boolean hayNoPasos() {
        for (Lance l : lances) {
            if (l instanceof LanceEnvido || l instanceof LanceOrdago) {
                return true;
            }
        }
        return false;
    }

    public static boolean hayPar(Naipe[] naipes) {
        Set<VALOR> valorExiste = new HashSet<VALOR>();
        for (Naipe n : naipes) {
            if (valorExiste.contains(n.getValorMus())) {
                return true;
            } else {
                valorExiste.add(n.getValorMus());
            }
        }
        return false;
    }

    // 0, no hay, 1 = pareja, 2=medias, 3=duples
    public static int getValorPares(Naipe[] naipes) {
        Map<VALOR, Integer> pares = getMapaPares(naipes);
        int pareja = 0;
        for (Map.Entry<VALOR, Integer> me : pares.entrySet()) {
            switch (me.getValue()) {
                case 2:
                    pareja++;
                    break;
                case 3:
                    return 2;
                case 4:
                    return 3;
            }
        }
        if (pareja == 2) {
            return 3;
        } else {
            return pareja;
        }
    }

    static Map<VALOR, Integer> puntosParesMus = new HashMap<VALOR, Integer>();

    static {
        puntosParesMus.put(VALOR.AS, 1);
        puntosParesMus.put(VALOR.DOS, 1);
        puntosParesMus.put(VALOR.TRES, 12);
        puntosParesMus.put(VALOR.CUATRO, 4);
        puntosParesMus.put(VALOR.CINCO, 5);
        puntosParesMus.put(VALOR.SEIS, 6);
        puntosParesMus.put(VALOR.SIETE, 7);
        puntosParesMus.put(VALOR.SOTA, 10);
        puntosParesMus.put(VALOR.CABALLO, 11);
        puntosParesMus.put(VALOR.REY, 12);
    }

    public static Map<VALOR, Integer> getMapaPares(Naipe[] naipes) {
        Map<VALOR, Integer> pares = new TreeMap<VALOR, Integer>(new Comparator<VALOR>() {
            public int compare(VALOR o1, VALOR o2) {
                return puntosParesMus.get(o1).compareTo(puntosParesMus.get(o2));
            }
        });
        for (Naipe n : naipes) {
            VALOR valor = n.getValorMus();
            int i = 1;
            if (pares.containsKey(valor)) {
                i += pares.get(valor);
            }
            pares.put(valor, i);
        }
        return pares;
    }

    public static void removeWithoutPar(Map<VALOR, Integer> mapaPar) {
        for (Map.Entry<VALOR, Integer> me : mapaPar.entrySet()) {
            if (me.getValue() == 1) {
                mapaPar.remove(me.getKey());
            }
        }
    }

    static Map<VALOR, Integer> puntosMus = new HashMap<VALOR, Integer>();

    static {
        puntosMus.put(VALOR.AS, 1);
        puntosMus.put(VALOR.DOS, 1);
        puntosMus.put(VALOR.TRES, 10);
        puntosMus.put(VALOR.CUATRO, 4);
        puntosMus.put(VALOR.CINCO, 5);
        puntosMus.put(VALOR.SEIS, 6);
        puntosMus.put(VALOR.SIETE, 7);
        puntosMus.put(VALOR.SOTA, 10);
        puntosMus.put(VALOR.CABALLO, 10);
        puntosMus.put(VALOR.REY, 10);
    }

    public static boolean hayJuego(Naipe[] naipes) {
        return cuentaPuntos(naipes) > 30;
    }

    public static int cuentaPuntos(Naipe[] naipes) {
        int puntos = 0;
        for (Naipe n : naipes) {
            puntos += puntosMus.get(n.getValor());
        }
        return puntos;
    }

    public boolean sumaPuntosPorJinete() {
        int lastValor = -1;
        int sumatorio = 0;
        int equipoGanador = -1;
        for (Lance l : lances) {
            if (l instanceof LanceEnvido) {
                int cantidad = ((LanceEnvido) l).getCantidad();
                sumatorio += cantidad;
                lastValor = cantidad;
                equipoGanador = l.getJugador().equipo;
            } else if (l instanceof LanceOrdago) {
                lastValor = 0;
                equipoGanador = l.getJugador().equipo;
            }
        }
        if (sumatorio - lastValor == 0) {
            // suma 1, por ji�ete
            getJugarMus().accionMesa(Accion.MENSAJE_TEXTO, "Equipo " + equipoGanador + " ganaa 1 por ji�ete");

            return ronda.juego.addPuntos(equipoGanador, 1);
        } else {

            getJugarMus().accionMesa(Accion.MENSAJE_TEXTO, "Equipo " + equipoGanador + " ganaa " + (sumatorio - lastValor) + "por ji�ete");
            return ronda.juego.addPuntos(equipoGanador, sumatorio - lastValor);
        }

    }

    public JugarMus getJugarMus() {
        return ronda.juego.vaca.partida.jugarMus;
    }

    public void cancelaRespuestaCompanero(JugadorMus jugadorMus) {
        getJugarMus().accionMesa(Accion.CANCELA_ENTRADA, jugadorMus.getPareja());
    }
}
